﻿using Serilog;
using System.Diagnostics;
using System.IO;
using System.IO.Compression;
using System.Text.Json;
using System.Windows;

namespace Update
{
    internal partial class Program
    {
        // 配置常量
        private const int MutexWaitTimeout = 10000; // 10秒等待超时
        private static string UpdateTempFolder { get; } = Path.Combine(AppContext.BaseDirectory, "tmp");

        [STAThread]
        static void Main()
        {
            var window = new MainWindow();
            try
            {
                window.Show();

                InitLogger();
                RunUpdateProcess();
            }
            catch (Exception ex)
            {
                ShowError($"更新过程中发生错误: {ex.Message}");
                LogError(ex);
            }
            finally
            {
                window.Close();
            }
        }

        /// <summary>
        /// 执行更新流程
        /// </summary>
        private static void RunUpdateProcess()
        {
            // 1. 获取更新元数据
            var updateInfo = LoadUpdateMetadata();
            if (updateInfo == null) return;

            // 2. 等待原应用退出
            if (!WaitForAppExit(updateInfo.AppId))
            {
                ShowError($"等待{updateInfo.AppName}退出超时，请手动关闭后重试");
                return;
            }

            // 3. 执行更新
            var success = ExecuteUpdate(updateInfo);
            if (!success) return;

            // 4. 启动新版本应用
            if (StartApplication(updateInfo))
            {
                // 延迟清理
                Thread.Sleep(3000);
                // 5. 清理临时文件
                CleanupTempFiles();
            }
        }

        private static UpdateInfo? LoadUpdateMetadata()
        {
            var metadataPath = Path.Combine(UpdateTempFolder, "UpdateMetadata.json");

            if (!File.Exists(metadataPath))
            {
                ShowError("更新元数据文件不存在");
                LogMessage($"错误：元数据文件不存在 {metadataPath}");
                return null;
            }

            try
            {
                var json = File.ReadAllText(metadataPath);
                return JsonSerializer.Deserialize<UpdateInfo>(json);
            }
            catch (Exception ex)
            {
                ShowError($"解析元数据失败: {ex.Message}");
                LogError(ex);
                return null;
            }
        }

        private static bool WaitForAppExit(string appId)
        {
            Mutex? mutex = null;
            try
            {
                // 尝试打开现有互斥体
                if (!Mutex.TryOpenExisting(appId, out mutex))
                {
                    LogMessage($"未找到互斥体 {appId}，认为应用已退出");
                    return true;
                }

                // 安全等待（处理AbandonedMutexException）
                bool acquired = false;
                try
                {
                    acquired = mutex.WaitOne(MutexWaitTimeout);
                }
                catch (AbandonedMutexException)
                {
                    LogMessage($"检测到被遗弃的互斥体 {appId}，认为原进程已异常退出");
                    return true; // 视为原进程已退出
                }

                if (acquired)
                {
                    mutex.ReleaseMutex();
                    LogMessage($"正常释放互斥体 {appId}");
                }
                else
                {
                    LogMessage($"等待互斥体 {appId} 超时");
                }

                return acquired;
            }
            catch (Exception ex)
            {
                LogError(ex);
                return false;
            }
            finally
            {
                mutex?.Dispose();
            }
        }

        private static bool ExecuteUpdate(UpdateInfo updateInfo)
        {
            try
            {
                // 优先删除主模块
                var mainModule = Path.Combine(AppContext.BaseDirectory, "main");

                if (Path.Exists(mainModule))
                {
                    Directory.Delete(mainModule, true);
                }

                var excludedDirectories = new List<string>();
                var updateModule = Path.Combine(AppContext.BaseDirectory, "update");

                // 如果 update 模块存在，则排除 update 模块
                // 因为 udpate 模块是有 mian 模块更新的
                // 如果 update 模块不存在，则说明是从没有 update 块的版本更新过来的
                // 那么需要拷贝 update 模块
                if (Path.Exists(updateModule))
                {
                    excludedDirectories.Add("update");
                }

                // 1. 更新程序包
                DirectoryCopier.CopyDirectory(
                    updateInfo.ExtractDirectory,
                    AppContext.BaseDirectory,
                    true,
                    excludedDirectories.ToArray(),
                    new string[] { "MatRec.exe" });

                return true;
            }
            catch (Exception ex)
            {
                ShowError($"更新过程中出错: {ex.Message}");
                LogError(ex);
                return false;
            }
        }

        private static bool StartApplication(UpdateInfo updateInfo)
        {
            try
            {
                LogMessage($"启动应用程序: {updateInfo.ExeFile}");

                var startInfo = new ProcessStartInfo
                {
                    FileName = updateInfo.ExeFile,
                    WorkingDirectory = updateInfo.AppDirectory,
                    UseShellExecute = true
                };

                Process.Start(startInfo);

                return true;
            }
            catch (Exception ex)
            {
                LogError(ex);
                ShowError($"启动应用程序失败: {ex.Message}");
                return false;
            }
        }

        private static void CleanupTempFiles()
        {
            try
            {
                if (Directory.Exists(UpdateTempFolder))
                {
                    LogMessage($"清理临时目录: {UpdateTempFolder}");
                    Directory.Delete(UpdateTempFolder, true);
                    LogMessage("临时目录清理完成");
                }
            }
            catch (Exception ex)
            {
                LogError(ex);
                LogMessage($"警告：清理临时文件失败: {ex.Message}");
            }
        }

        private static void ShowError(string message)
        {
            MessageBox.Show(message, "更新错误", MessageBoxButton.OK, MessageBoxImage.Error);
            LogMessage($"用户显示错误: {message}");
        }

        private static void LogError(Exception ex)
        {
            Log.Logger.Error(ex, "更新错误");
        }

        private static void LogMessage(string message)
        {
            try
            {
                Log.Logger.Information(message);
                Debug.WriteLine(message); // 同时输出到调试窗口
            }
            catch
            {
                // 如果日志写入失败，忽略不影响主流程
            }
        }

        private static void InitLogger()
        {
            Log.Logger = new LoggerConfiguration()
                .Enrich.FromLogContext()
#if RELEASE
                .MinimumLevel.Information()
#else
                .MinimumLevel.Debug()
#endif
                .WriteTo.File(
                    path: $"logs/update/log-.txt",
                    rollingInterval: RollingInterval.Day,
                    rollOnFileSizeLimit: true,
                    retainedFileCountLimit: 7,
                    outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}] {Message:lj}{NewLine}{Exception}",
                    fileSizeLimitBytes: 10 * 1024 * 1024
                )
                .CreateLogger();
        }
    }
}